# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _, tools
from odoo.exceptions import UserError
import json


class Ods2SaleWizard(models.TransientModel):
    _name = "fp.ods2sale.wizard"
    _description = "Ods to Sales Wizard"

    invoice_type = fields.Selection(
        selection=[
            # ('vat_fapiao', '增值税发票'),  # 草稿状态时, 增值税发票的类型待定
            ('vat_special', '增值税专用发票'),
            ('vat_general', '增值税普通发票'),
            ('vat_general_e', '增值税电子普通发票'),
            ('vat_special_e', '增值税电子专用发票'),
            # ('40', '货物运输业增值税专用发票'),
            # ('50', '机动车销售统一发票'),
            # ('60', '卷式发票'),
            # ('70', '二手车发票'),
            # ('80', '通行费发票'),
        ], default='vat_general_e')

    is_merge = fields.Boolean(default=False)

    merge_type = fields.Selection([
        ('merge_todo', 'Merge Todo'),
        ('push_plus', 'Push Plus'),
        ('merge_plus', 'Merge Plus'),
        ('merge_neg', 'Merge Neg'),
        # ('merge_all', 'Merge All'),
        ('split_discount', 'Split Discount'),
        # ('merge_split_discount', 'Merge And Split Discount'),

    ], string='Merge Type', default='merge_todo')

    merge_check = fields.Selection([
        ('ok', 'Ok'),
        ('error', 'Error'),
        ('merge_plus_error', 'Merge Plus Error'),
        ('merge_neg_error', 'Merge Neg Error'),
        ('split_discount_error', 'Split Discount Error'),
        ('push_plus_error', 'Push Plus Error'),
    ], string='Merge Check')

    ods_line_ids = fields.Many2many(
        'fp.ods.sale.order.line',
        'fp_ods2sale_ods_line_rel',
        'wizard_id', 'line_id',
        string="Ods Lines", copy=False,)

    line_ids = fields.One2many(
        'fp.ods2sale.wizard.line', 'wizard_id',
        string="Lines", copy=False)

    can_do = fields.Boolean()

    neg_lines_count = fields.Integer(string='Neg Lines Count')
    amount_discount = fields.Float(string='Discount Amount')
    amount_discount_done = fields.Float(string='Discount Amount Done')

    @api.onchange('line_ids', 'line_ids.amount_discount')
    def _onchange_line_ids(self):
        for one in self:
            one._set_amount()

    @api.onchange('merge_type')
    def _onchange_for_merge_type(self):
        for one in self:
            one._onchange_for_merge_type_one()

    def _onchange_for_merge_type_one(self):
        merge_type = self.merge_type

        if merge_type in ['merge_neg', 'merge_plus']:
            self.is_merge = True
        elif merge_type in ['push_plus', 'merge_todo']:
            self.is_merge = False

        lines = self.line_ids._onchange_for_merge_type_one(
            merge_type, self.is_merge, self.ods_line_ids)
        self.merge_check = self._get_merge_check()
        self.line_ids = [(5, 0, 0)] + [(0, 0, vals_line)
                                       for vals_line in lines]

        self._set_amount()

    def _set_amount(self):
        discount_prd = self.env['fp.product'].discount_get()
        olds_discount = self.ods_line_ids.filtered(
            lambda r: r.product_id.id == discount_prd.id)

        self.amount_discount = round(
            sum(olds_discount.mapped('amount_total')), 2)

        self.amount_discount_done = round(
            sum(self.line_ids.mapped('amount_discount')), 2)

        neg_line_ids = self.line_ids.filtered(
            lambda r: r.amount_total + r.amount_discount < 0 or r.amount_total < 0)
        self.neg_lines_count = len(neg_line_ids)

        if not self.neg_lines_count and not self.amount_discount - self.amount_discount_done:
            self.can_do = True
        else:
            self.can_do = False

    def _get_merge_check(self):
        merge_type = self.merge_type
        ods_lines = self.ods_line_ids

        discount_prd = self.env['fp.product'].discount_get()

        olds_plus = ods_lines.filtered(lambda r: r.quantity >= 0)
        olds_neg = ods_lines.filtered(
            lambda r: r.quantity < 0 and r.product_id.id != discount_prd.id)

        olds_discount = ods_lines.filtered(
            lambda r: r.product_id.id == discount_prd.id)

        merge_check = None

        if merge_type == 'merge_plus':
            if olds_plus and not olds_neg and not olds_discount:
                merge_check = 'ok'
            else:
                merge_check = 'merge_plus_error'

        elif merge_type in ['merge_neg', 'merge_all']:
            if olds_plus and olds_neg and not olds_discount:
                merge_check = 'ok'
            else:
                merge_check = 'merge_neg_error'

        elif merge_type in ['split_discount', 'merge_split_discount']:
            if olds_plus and olds_discount:
                merge_check = 'ok'
            else:
                merge_check = 'split_discount_error'
        else:  # merge_type == 'push_plus':

            if olds_plus and not olds_neg and not olds_discount:
                merge_check = 'ok'
            else:
                merge_check = 'push_plus_error'

        return merge_check

    @api.model
    def _values_get_by_ods(self, ods_orders):
        ods_lines = ods_orders.mapped('line_ids')

        ods_lines = ods_lines.sorted(lambda l: (
            l.order_partner_id.id, l.order_num, l.sequence))

        return {
            'ods_line_ids': [(6, 0, ods_lines.ids)],
        }

    def onchange(self, values, field_name, field_onchange):
        result = super().onchange(values, field_name, field_onchange)

        if field_name:
            return result

        active_ids = self._context.get('active_ids', [])
        ods_obj = self.env['fp.ods.sale.order']
        ods_orders = ods_obj.browse(active_ids)

        default_vals = self._values_get_by_ods(ods_orders)

        result['value'].update(default_vals)
        return result

    def _values_list_get_for_sale_order(self):

        todo_dict = {}

        for ln in self.line_ids:
            company_id = ln.company_id.id
            user_id = ln.user_id.id
            order_partner_id = ln.order_partner_id.id
            nm = ','.join(
                map(lambda i: str(i), [company_id, user_id, order_partner_id]))

            todo_dict[nm] = (company_id, user_id, order_partner_id)

        values_list = []

        for todo in todo_dict.values():
            company_id, user_id, order_partner_id = todo
            ptn_lines = self.line_ids.filtered(
                lambda r: r.order_partner_id.id == order_partner_id
                and r.company_id.id == company_id and r.user_id.id == user_id)

            ods_line_ids_jsons = ptn_lines.mapped('ods_line_ids')

            ods_line_ids = []
            for lnid in ods_line_ids_jsons:
                ods_line_ids = ods_line_ids + json.loads(lnid)

            ods_lines = self.ods_line_ids.browse(ods_line_ids)
            ods_orders = ods_lines.mapped('order_id')

            ods_order_ids_json = json.dumps(ods_orders.ids)

            client_order_ref = ','.join(
                filter(lambda c: c, ods_orders.mapped('client_order_ref')))

            values_list_lines = ptn_lines._values_list_get_for_sale_order()

            vals = {
                'company_id': company_id,
                'user_id':   user_id,
                'partner_id':   order_partner_id,

                'invoice_type': self.invoice_type,
                'client_order_ref': client_order_ref,
                'ods_order_ids_json': ods_order_ids_json,
                'line_ids': [(0, 0, vals_line)
                             for vals_line in values_list_lines]

            }
            values_list.append(vals)

        return values_list

    def create_sale_order(self):
        if not self.can_do:
            raise UserError("error, 请检查数据, 不能创建销售订单: 负数行=%d,  未分配的折扣额度=%d" %
                            (self.neg_lines_count, self.amount_discount - self.amount_discount_done))

        values_list = self._values_list_get_for_sale_order()

        sale_order_obj = self.env['fp.sale.order']
        orders = sale_order_obj.create(values_list)

        for order in orders:
            oids = json.loads(order.ods_order_ids_json)
            self.env['fp.ods.sale.order'].browse(oids).write({
                'sale_order_id': order.id,
                'state': 'done'
            })

        return orders.ids


class Ods2SaleLine(models.TransientModel):
    _name = "fp.ods2sale.wizard.line"
    _inherit = ['fp.sale_line.mixin']

    _description = "Ods2Sales Line"

    wizard_id = fields.Many2one(
        'fp.ods2sale.wizard', string='wizard',
        required=True, ondelete='cascade', index=True, copy=False)

    ods_line_ids = fields.Char()

    company_id = fields.Many2one('res.company', 'Company', required=True)
    user_id = fields.Many2one('res.users', string='User', required=True)
    order_partner_id = fields.Many2one(
        'fp.biz.partner', string='Customer', required=True)

    name = fields.Text(string='Description', required=True)
    client_line_ref = fields.Char(string='Customer Reference', copy=False)

    order_num = fields.Char('NC订单号')
    # logistics_num = fields.Char('配载号')
    product_lot = fields.Char(string='批号')

    sequence = fields.Integer(string='Sequence', default=10)
    note = fields.Text()

    def _onchange_for_merge_type_one_merge_plus(self, ods_line_ids, is_neg=None):
        values_list_all = self._onchange_for_merge_type_one_plus(ods_line_ids)

        values_list_new = []

        def _search_in_new(vals):
            if not values_list_new:
                return None

            fields = [
                'company_id',
                'user_id',
                'order_partner_id',
                'product_id',
                'tax_ratio',
                'price_unit',
            ]

            if is_neg:
                fields.append('product_lot')

            for vn in values_list_new:
                check = True
                for fld in fields:
                    check = check and vn[fld] == vals[fld]

                if check:
                    return vn

            return None

        for vals in values_list_all:

            vals2 = _search_in_new(vals)

            if not vals2:
                vals2 = {}  # vals.copy()
                vals2.update({
                    'company_id': vals['company_id'],
                    'user_id': vals['user_id'],
                    'order_partner_id': vals['order_partner_id'],
                    'client_line_ref': '',
                    'order_num': '',
                    'sequence': 0,
                    'name': '',
                    'note': '',
                    'product_id': vals['product_id'],
                    'product_lot': vals['product_lot'],
                    'tax_ratio': vals['tax_ratio'],
                    'price_unit': vals['price_unit'],
                    'quantity': 0,
                    'amount_discount': 0,
                    # 'amount_untaxed': 0,
                    # 'amount_tax': 0,
                    # 'amount_total': 0,
                    'ods_line_ids': []
                })

                values_list_new.append(vals2)

            for fld in ['client_line_ref', 'order_num']:
                if vals2[fld] and vals[fld]:
                    vals2[fld] = vals2[fld] + ',' + vals[fld]
                elif vals2[fld] or vals[fld]:
                    vals2[fld] = vals2[fld] or vals[fld]

            for fld in ['name', 'note']:
                vals2[fld] = vals2[fld] or vals[fld]

            vals2.update({
                'client_line_ref':  vals2['client_line_ref'],
                'order_num': vals2['order_num'],
                'sequence': vals2['sequence'] or vals['sequence'],
                'name':  vals2['name'],
                'note':   vals2['note'],
                'quantity': vals2['quantity'] + vals['quantity'],
                'amount_discount':  vals2['amount_discount'] + vals['amount_discount'],
                # 'amount_untaxed':  vals2['amount_untaxed'] + vals['amount_untaxed'],
                # 'amount_tax':  vals2['amount_tax'] + vals['amount_tax'],
                # 'amount_total':  vals2['amount_total'] + vals['amount_total'],
                'ods_line_ids':  vals2['ods_line_ids'] + vals['ods_line_ids']
            })

        return values_list_new

    def _onchange_for_merge_type_one_plus(self, ods_line_ids):
        ods_lines = ods_line_ids

        for_write = True

        def m2o_get(m2o):
            if m2o:
                if for_write:
                    return m2o.id
                else:
                    return [m2o.id, m2o.display_name]
            else:
                return m2o

        values_list = []
        for one in ods_lines:

            vals = {
                'company_id': m2o_get(one.company_id),
                'user_id': m2o_get(one.user_id),
                'order_partner_id': m2o_get(one.order_partner_id),
                'name': one.name,
                'client_line_ref': one.client_line_ref,
                'order_num': one.order_num,
                'sequence': one.sequence,
                'note': one.note,
                'product_id': m2o_get(one.product_id),
                'product_lot': one.product_lot,


                'tax_ratio': one.tax_ratio,
                'quantity': one.quantity,
                'price_unit': one.price_unit,
                'amount_discount': one.amount_discount,
                # 'amount_untaxed': one.amount_untaxed,
                # 'amount_tax': one.amount_tax,
                # 'amount_total': one.amount_total,

                'ods_line_ids': one.ids
            }

            values_list.append(vals)

        return values_list

    def _onchange_for_merge_type_one_discount(self, values_list, olds_discount):
        amount_total = sum(
            map(lambda one:  one['price_unit'] * one['quantity'], values_list))

        # amount_discount_total = sum(
        #     map(lambda one:  one['price_unit'] * one['quantity'], olds_discount))

        amount_discount_total = sum(olds_discount.mapped('amount_total'))

        if not values_list or not amount_total or not amount_discount_total:
            return values_list

        for one in values_list:
            one_total = one['price_unit'] * one['quantity']
            one_discount = amount_discount_total * one_total / amount_total
            one_discount = round(one_discount, 2)
            one['amount_discount'] = one_discount
            one['ods_line_ids'] = one['ods_line_ids'] + olds_discount.ids

        amount_discount_total2 = sum(
            map(lambda one: one['amount_discount'], values_list))

        if amount_discount_total == amount_discount_total2:
            return values_list

        else:
            diff = amount_discount_total - amount_discount_total2
            values_list[0]['amount_discount'] = values_list[0]['amount_discount'] + diff
            return values_list

    def _onchange_for_merge_type_one(self, merge_type, is_merge, ods_line_ids):
        ods_lines = ods_line_ids

        discount_prd = self.env['fp.product'].discount_get()

        olds_plus = ods_lines.filtered(
            lambda r: r.product_id.id != discount_prd.id and r.quantity >= 0)

        # olds_neg = ods_lines.filtered(
        #     lambda r: r.quantity < 0 and r.product_id.id != discount_prd.id)

        olds_all = ods_lines.filtered(
            lambda r: r.product_id.id != discount_prd.id)

        olds_discount = ods_lines.filtered(
            lambda r: r.product_id.id == discount_prd.id)

        values_list = []

        if merge_type in['merge_plus']:
            # 6要素合并
            values_list = self._onchange_for_merge_type_one_merge_plus(
                olds_plus)

        elif merge_type in['merge_neg']:
            # 7要素合并
            values_list = self._onchange_for_merge_type_one_merge_plus(
                olds_all, True)

        elif merge_type in['split_discount']:
            if is_merge:
                values_list = self._onchange_for_merge_type_one_merge_plus(
                    olds_all)
            else:
                values_list = self._onchange_for_merge_type_one_plus(
                    olds_all)

            values_list = self._onchange_for_merge_type_one_discount(
                values_list, olds_discount)

        else:  # merge_type in['merge_todo', 'push_plus']:
            values_list = self._onchange_for_merge_type_one_plus(olds_plus)

        for vals in values_list:
            vals['ods_line_ids'] = json.dumps(vals['ods_line_ids'])

        return values_list

    def _values_list_get_for_sale_order(self):

        values_list = []
        for line in self:

            ods_line_ids = json.loads(line.ods_line_ids)

            vals = {
                'client_line_ref': line.client_line_ref,
                'order_num': line.order_num,
                'name': line.name,

                'sequence': line.sequence,
                'note': line.note,

                'ods_line_ids': [(6, 0, ods_line_ids)],
                'product_id': line.product_id.id,
                'tax_ratio': line.tax_ratio,
                'quantity': line.quantity,
                'price_unit': line.price_unit,
                'amount_discount': line.amount_discount
            }

            values_list.append(vals)

        return values_list
